import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:learning_content_admin_pannel.core/core/domain/entities/learning_content/lexical_item/lexical_item.dart';
import 'package:learning_content_admin_pannel.core/core/domain/entities/page_models/single_word_base_page_model.dart';
import 'package:learning_content_admin_pannel.core/core/domain/utils/either_extension.dart';
import 'package:learning_content_admin_pannel.core/core/domain/utils/with_previous_stream.dart';
import 'package:learning_content_admin_pannel.core/learning_content/application/bloc/bloc.dart';
import 'package:learning_content_admin_pannel.core/root_navigator/application/bloc/bloc.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/entities/part_of_speech.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/entities/word_base_input.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/add_use_case_input/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/change_word_image/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/delete_use_case_input/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/delete_word_image/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/get_initial_data/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_cached_lexical_item_image_path_map_outer_update/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_learning_content_outer_update/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_lexical_item_image_bank_update/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_next_word_base_page_opening_request/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_page_closing_request/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/handle_previous_word_base_page_opening_request/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/initialize/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/promote_use_case_input/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/swap_use_case_inputs/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_use_case_input_example_markdown/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_am_transcription/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_br_transcription/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_cefr_level/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_definition/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_part_of_speech/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_rank/use_case.dart';
import 'package:learning_content_admin_pannel.home.words/single_word_base_page/domain/use_cases/update_word_base_input_title/use_case.dart';

part 'bloc_state.dart';

class SingleWordBasePageBloc extends Cubit<SingleWordBasePageBlocState> {
  SingleWordBasePageBloc({
    required SingleWordBasePageModel pageModel,
    required LearningContentBloc learningContentBloc,
    required RootNavigatorBloc rootNavigatorBloc,
    required GetInitialData getInitialData,
    required Initialize initialize,
    required AddUseCaseInput addUseCaseInput,
    required ChangeWordImage changeWordImage,
    required DeleteUseCaseInput deleteUseCaseInput,
    required DeleteWordImage deleteWordImage,
    required HandleCachedLexicalItemImagePathMapOuterUpdate
        handleCachedLexicalItemImagePathMapOuterUpdate,
    required HandleLearningContentOuterUpdate handleLearningContentOuterUpdate,
    required HandleLexicalItemImageBankOuterUpdate handleLexicalItemImageBankOuterUpdate,
    required HandleNextWordBasePageOpeningRequest handleNextWordBasePageOpeningRequest,
    required HandlePageClosingRequest handlePageClosingRequest,
    required HandlePreviousWordBasePageOpeningRequest handlePreviousWordBasePageOpeningRequest,
    required PromoteUseCaseInput promoteUseCaseInput,
    required SwapUseCaseInputs swapUseCaseInputs,
    required UpdateUseCaseInputExampleMarkdown updateUseCaseInputExampleMarkdown,
    required UpdateWordBaseInputAmTranscription updateWordBaseInputAmTranscription,
    required UpdateWordBaseInputBrTranscription updateWordBaseInputBrTranscription,
    required UpdateWordBaseInputCefrLevel updateWordBaseInputCefrLevel,
    required UpdateWordBaseInputDefinition updateWordBaseInputDefinition,
    required UpdateWordBaseInputPartOfSpeech updateWordBaseInputPartOfSpeech,
    required UpdateWordBaseInputRank updateWordBaseInputRank,
    required UpdateWordBaseInputTitle updateWordBaseInputTitle,
  })  : _pageModel = pageModel,
        _learningContentBloc = learningContentBloc,
        _rootNavigatorBloc = rootNavigatorBloc,
        _addUseCaseInput = addUseCaseInput,
        _changeWordImage = changeWordImage,
        _deleteUseCaseInput = deleteUseCaseInput,
        _deleteWordImage = deleteWordImage,
        _handleCachedLexicalItemImagePathMapOuterUpdate =
            handleCachedLexicalItemImagePathMapOuterUpdate,
        _handleLearningContentOuterUpdate = handleLearningContentOuterUpdate,
        _handleLexicalItemImageBankOuterUpdate = handleLexicalItemImageBankOuterUpdate,
        _handleNextWordBasePageOpeningRequest = handleNextWordBasePageOpeningRequest,
        _handlePageClosingRequest = handlePageClosingRequest,
        _handlePreviousWordBasePageOpeningRequest = handlePreviousWordBasePageOpeningRequest,
        _promoteUseCaseInput = promoteUseCaseInput,
        _swapUseCaseInputs = swapUseCaseInputs,
        _updateUseCaseInputExampleMarkdown = updateUseCaseInputExampleMarkdown,
        _updateWordBaseInputAmTranscription = updateWordBaseInputAmTranscription,
        _updateWordBaseInputBrTranscription = updateWordBaseInputBrTranscription,
        _updateWordBaseInputCefrLevel = updateWordBaseInputCefrLevel,
        _updateWordBaseInputDefinition = updateWordBaseInputDefinition,
        _updateWordBaseInputPartOfSpeech = updateWordBaseInputPartOfSpeech,
        _updateWordBaseInputRank = updateWordBaseInputRank,
        _updateWordBaseInputTitle = updateWordBaseInputTitle,
        super(_getInitialState(
          pageModel: pageModel,
          learningContentBlocState: learningContentBloc.state!,
          getInitialData: getInitialData,
        )) {
    streamWithPrevious = stream.withPreviousSeeded(state);

    final initializationResult = initialize(
      wordId: pageModel.wordId,
      lexicalItemMap: learningContentBloc.state!.lexicalItemMap,
    );

    lexicalItemImageBankImagePathStreamSubscription = initializationResult
        .lexicalItemImageBankImagePathStream
        .listen(_onLexicalItemImageBankUpdated);

    _cachedImagePathMapStreamSubscription = initializationResult.cachedLexicalItemImagePathMapStream
        .listen(_onCachedLexicalItemImagePathMapChanged);

    _learningContentBlocSubscription =
        learningContentBloc.stream.listen(_onLearningContentBlocStateChanged);
  }

  final SingleWordBasePageModel _pageModel;

  final LearningContentBloc _learningContentBloc;
  final RootNavigatorBloc _rootNavigatorBloc;

  final AddUseCaseInput _addUseCaseInput;
  final ChangeWordImage _changeWordImage;
  final DeleteUseCaseInput _deleteUseCaseInput;
  final DeleteWordImage _deleteWordImage;
  final HandleCachedLexicalItemImagePathMapOuterUpdate
      _handleCachedLexicalItemImagePathMapOuterUpdate;
  final HandleLearningContentOuterUpdate _handleLearningContentOuterUpdate;
  final HandleLexicalItemImageBankOuterUpdate _handleLexicalItemImageBankOuterUpdate;
  final HandleNextWordBasePageOpeningRequest _handleNextWordBasePageOpeningRequest;
  final HandlePageClosingRequest _handlePageClosingRequest;
  final HandlePreviousWordBasePageOpeningRequest _handlePreviousWordBasePageOpeningRequest;
  final PromoteUseCaseInput _promoteUseCaseInput;
  final SwapUseCaseInputs _swapUseCaseInputs;
  final UpdateUseCaseInputExampleMarkdown _updateUseCaseInputExampleMarkdown;
  final UpdateWordBaseInputAmTranscription _updateWordBaseInputAmTranscription;
  final UpdateWordBaseInputBrTranscription _updateWordBaseInputBrTranscription;
  final UpdateWordBaseInputCefrLevel _updateWordBaseInputCefrLevel;
  final UpdateWordBaseInputDefinition _updateWordBaseInputDefinition;
  final UpdateWordBaseInputPartOfSpeech _updateWordBaseInputPartOfSpeech;
  final UpdateWordBaseInputRank _updateWordBaseInputRank;
  final UpdateWordBaseInputTitle _updateWordBaseInputTitle;

  late final Stream<(SingleWordBasePageBlocState, SingleWordBasePageBlocState)> streamWithPrevious;
  late final StreamSubscription<BuiltSet<String>> lexicalItemImageBankImagePathStreamSubscription;
  late final StreamSubscription<BuiltMap<String, String>> _cachedImagePathMapStreamSubscription;
  late final StreamSubscription<LearningContentBlocState> _learningContentBlocSubscription;

  static SingleWordBasePageBlocState _getInitialState({
    required SingleWordBasePageModel pageModel,
    required LearningContentBlocState learningContentBlocState,
    required GetInitialData getInitialData,
  }) {
    final result = getInitialData(
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      wordId: pageModel.wordId,
    );

    return SingleWordBasePageBlocState(
      cachedWordImagePath: result.cachedWordImagePath,
      wordBaseInput: result.wordBaseInput,
      suggestedWordImagePaths: result.suggestedWordImagePaths,
    );
  }

  void _onLexicalItemImageBankUpdated(BuiltSet<String> imagePaths) {
    final result = _handleLexicalItemImageBankOuterUpdate(
      wordId: _pageModel.wordId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      imagePaths: imagePaths,
    );

    emit(state.copyWith(
      suggestedWordImagePaths: () => result.suggestedWordImagePaths,
    ));
  }

  void _onCachedLexicalItemImagePathMapChanged(
      BuiltMap<String, String> cachedLexicalItemImagePathMap) {
    final result = _handleCachedLexicalItemImagePathMapOuterUpdate(
      wordId: _pageModel.wordId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      cachedLexicalItemImagePathMap: cachedLexicalItemImagePathMap,
    );

    if (state.cachedWordImagePath == result.cachedWordImagePath) {
      return;
    }

    emit(state.copyWith(
      cachedWordImagePath: () => result.cachedWordImagePath,
    ));
  }

  void _onLearningContentBlocStateChanged(LearningContentBlocState learningContentBlocState) {
    final failureOrResult = _handleLearningContentOuterUpdate(
      pageModel: _pageModel,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      rootNavigatorPageModels: _rootNavigatorBloc.state.pageModels,
      rootNavigatorPageModelToTransitionInfo: _rootNavigatorBloc.state.pageModelToTransitionInfo,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case NonexistentWordLearningContentOuterUpdateHandlingFailure():
          _rootNavigatorBloc.requestPageStackUpdate(
            pageModels: failure.rootNavigatorPageModels,
            pageModelToTransitionInfo: failure.rootNavigatorPageModelToTransitionInfo,
          );
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(state.copyWith(
      cachedWordImagePath: () => result.cachedWordImagePath,
    ));
  }

  void requestPageClosing() {
    final result = _handlePageClosingRequest(
      pageModel: _pageModel,
      wordBaseInput: state.wordBaseInput,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      rootNavigatorPageModels: _rootNavigatorBloc.state.pageModels,
      rootNavigatorPageModelToTransitionInfo: _rootNavigatorBloc.state.pageModelToTransitionInfo,
    );

    _rootNavigatorBloc.requestPageStackUpdate(
      pageModels: result.rootNavigatorPageModels,
      pageModelToTransitionInfo: result.rootNavigatorPageModelToTransitionInfo,
    );

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestPreviousWordBasePageOpening() {
    final result = _handlePreviousWordBasePageOpeningRequest(
      pageModel: _pageModel,
      wordBaseInput: state.wordBaseInput,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      rootNavigatorPageModels: _rootNavigatorBloc.state.pageModels,
      rootNavigatorPageModelToTransitionInfo: _rootNavigatorBloc.state.pageModelToTransitionInfo,
    );

    _rootNavigatorBloc.requestPageStackUpdate(
      pageModels: result.rootNavigatorPageModels,
      pageModelToTransitionInfo: result.rootNavigatorPageModelToTransitionInfo,
    );

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestNextWordBasePageOpening() {
    final result = _handleNextWordBasePageOpeningRequest(
      pageModel: _pageModel,
      wordBaseInput: state.wordBaseInput,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
      rootNavigatorPageModels: _rootNavigatorBloc.state.pageModels,
      rootNavigatorPageModelToTransitionInfo: _rootNavigatorBloc.state.pageModelToTransitionInfo,
    );

    _rootNavigatorBloc.requestPageStackUpdate(
      pageModels: result.rootNavigatorPageModels,
      pageModelToTransitionInfo: result.rootNavigatorPageModelToTransitionInfo,
    );

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestUseCaseInputAddition() {
    final result = _addUseCaseInput(
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestUseCaseInputDeletion({
    required String useCaseInputId,
  }) {
    final result = _deleteUseCaseInput(
      useCaseInputId: useCaseInputId,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestUseCaseInputPromotion({
    required String useCaseInputId,
  }) {
    final result = _promoteUseCaseInput(
      useCaseInputId: useCaseInputId,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestUseCaseInputSwap({
    required String firstUseCaseInputId,
    required String secondUseCaseInputId,
  }) {
    final result = _swapUseCaseInputs(
      firstUseCaseInputId: firstUseCaseInputId,
      secondUseCaseInputId: secondUseCaseInputId,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestUseCaseInputExampleMarkdownUpdate({
    required String useCaseInputId,
    required String useCaseExampleMarkdownInputText,
  }) {
    final result = _updateUseCaseInputExampleMarkdown(
      useCaseInputId: useCaseInputId,
      useCaseInputExampleMarkdownInputText: useCaseExampleMarkdownInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputAmTranscriptionUpdate({
    required String amTranscriptionInputText,
  }) {
    final result = _updateWordBaseInputAmTranscription(
      amTranscriptionInputText: amTranscriptionInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputBrTranscriptionUpdate({
    required String brTranscriptionInputText,
  }) {
    final result = _updateWordBaseInputBrTranscription(
      brTranscriptionInputText: brTranscriptionInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputCefrLevelUpdate({
    required CefrLevel cefrLevel,
  }) {
    final result = _updateWordBaseInputCefrLevel(
      wordBaseInputCefrLevel: cefrLevel,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputDefinitionUpdate({
    required String definitionInputText,
  }) {
    final result = _updateWordBaseInputDefinition(
      wordBaseInputDefinitionInputText: definitionInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputPartOfSpeechUpdate({
    required PartOfSpeech partOfSpeech,
  }) {
    final result = _updateWordBaseInputPartOfSpeech(
      wordBaseInputPartOfSpeech: partOfSpeech,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputRankUpdate({
    required String rankInputText,
  }) {
    final result = _updateWordBaseInputRank(
      wordBaseInputRankInputText: rankInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordBaseInputTitleUpdate({
    required String titleInputText,
  }) {
    final result = _updateWordBaseInputTitle(
      wordBaseInputTitleInputText: titleInputText,
      wordBaseInput: state.wordBaseInput,
    );

    emit(state.copyWith(
      wordBaseInput: () => result.wordBaseInput,
    ));
  }

  void requestWordImageChange({
    required String imagePath,
  }) async {
    final failureOrResult = await _changeWordImage(
      wordId: _pageModel.wordId,
      imagePath: imagePath,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    if (failureOrResult.isLeft()) {
      return;
    }

    final result = failureOrResult.asRight();

    emit(state.copyWith(
      cachedWordImagePath: () => result.cachedWordImagePath,
    ));

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestWordImageDeletion() async {
    final failureOrResult = await _deleteWordImage(
      wordId: _pageModel.wordId,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    if (failureOrResult.isLeft()) {
      return;
    }

    final result = failureOrResult.asRight();

    emit(state.copyWith(
      cachedWordImagePath: () => result.cachedWordImagePath,
    ));

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  @override
  Future<void> close() {
    lexicalItemImageBankImagePathStreamSubscription.cancel();
    _cachedImagePathMapStreamSubscription.cancel();
    _learningContentBlocSubscription.cancel();
    return super.close();
  }
}
